﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Data.Imaging;

namespace RayDen.Library.Components.Surface.Spectral
{
    [Serializable]
    public class AlloyMaterial : SpectralBrdf
    {
        public override BrdfType Type
        {
            get { return BrdfType.Glossy; }
        }

        public SpectralDistribution Krefl, Kdiff;

        public RgbSpectrum KdiffOverPI;
        public float exponent;
        public float R0;
        public bool reflectionSpecularBounce;
        public AlloyMaterial(RgbSpectrum col, RgbSpectrum refl,
                             float exp, float schlickTerm, bool reflSpecularBounce)
        {
            Krefl = ColorFactory.CreateRegularSpd(ref refl, SpectrumType.Reflectance);
            Kdiff = ColorFactory.CreateRegularSpd(ref col, SpectrumType.Reflectance);
            //KdiffOverPI = Kdiff * MathLab.INVPI;
            R0 = schlickTerm;
            exponent = 1f / (exp + 1f);

            reflectionSpecularBounce = reflSpecularBounce;
        }

        
        public override bool IsDiffuse()
        {
            return true;
        }

        public override bool IsSpecular()
        {
            return true;
        }

        public override void F(ref Vector wo, ref Vector wi, ref Normal N, float lambda, out float fs)
        {
            float c = 1f - Vector.Dot(ref wo, ref N);
            float Re = R0 + (1f - R0) * c * c * c * c * c;

            float P = .25f + .5f * Re;

            fs = Kd.Sample(lambda)*MathLab.INVPI * (1f - Re) / (1f - P);
        }

        public override void Sample_f(ref Vector wo, ref Normal geoNormal, ref Normal shadeN, float lambda, ref SurfaceTextureData surfaceData,
            float u0, float u1, float u2, out BsdfSampleData result)
        {
            float c = 1f - Vector.Dot(ref wo, ref shadeN);
            float Re = R0 + (1f - R0) * c * c * c * c * c;
            result = new BsdfSampleData();
            float P = .25f + .5f * Re;

            if ((u2 < P))
            {//onlySpecular || 
                result.Wi = SurfaceBsdf.GlossyReflection(ref wo, exponent, ref shadeN, u0, u1);
                result.Pdf = P / Re;
                result.Type = reflectionSpecularBounce ? BrdfType.Specular : BrdfType.Diffuse;
                //result.F = Re * Krefl;
                result.Lambda = Krefl.Sample(lambda)*Re;
            }
            else
            {
                Vector dir = MC.CosineSampleHemisphere(u0, u1);
                result.Pdf = dir.z * MathLab.INVPI;

                Vector v1, v2;
                Vector.CoordinateSystem(ref shadeN, out v1, out v2);

                dir = new Vector(
                    v1.x * dir.x + v2.x * dir.y + shadeN.x * dir.z,
                    v1.y * dir.x + v2.y * dir.y + shadeN.y * dir.z,
                    v1.z * dir.x + v2.z * dir.y + shadeN.z * dir.z);

                var wi = dir;

                float dp = Vector.Dot(ref shadeN, ref wi);
                // Using 0.0001 instead of 0.0 to cut down fireflies
                if (dp <= 0.0001f)
                {
                    result.Pdf = 0f;
                    result.Type = reflectionSpecularBounce ? BrdfType.Specular : BrdfType.Diffuse;
                    result.F = new RgbSpectrum();
                }
                result.Pdf /= dp;

                float iRe = 1f - Re;
                result.Pdf *= (1f - P) / iRe;
                result.Type = BrdfType.Glossy;
                result.Wi = wi;

                //result.F = iRe * Kdiff;
                result.Lambda = Kd.Sample(lambda)*iRe;

            }
        }
    }
}
